package com.zhanghe.cameraTest

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.ImageFormat
import android.graphics.SurfaceTexture
import android.hardware.camera2.*
import android.hardware.camera2.CameraCaptureSession.CaptureCallback
import android.media.MediaRecorder
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.HandlerThread
import android.util.Log
import android.util.Range
import android.util.Size
import android.view.Surface
import android.view.TextureView
import android.view.TextureView.SurfaceTextureListener
import android.view.View
import android.widget.Button
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import java.io.File
import java.util.*


class MainActivity : AppCompatActivity() {

    var TAG = "MainActivity";

    private var mChildHandler: Handler? = null

    private var mTextureView: TextureView? = null
    private var mCameraManager: CameraManager? = null
    private var mCurrentSelectCamera: String? = null

    private var mPreviewCaptureRequest: CaptureRequest.Builder? = null
    private var mCameraDevice: CameraDevice? = null
    private var mCameraDeviceStateCallback: CameraDevice.StateCallback? = null
    private var mSessionStateCallback: CameraCaptureSession.StateCallback? = null
    private var mCameraCaptureSession: CameraCaptureSession? = null
    private var mSessionCaptureCallback: CaptureCallback? = null
    private var mMediaRecorder: MediaRecorder? = null
    private var mBtnStatr: Button? = null
    private  var mBtnFinish:Button? = null

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mTextureView = findViewById(R.id.textureview2);
        mBtnStatr = findViewById(R.id.btn_start);
        mBtnFinish = findViewById(R.id.btn_finish);
        initClickListener();
        initChildHandler();
        initTextureViewStateListener();
        initMediaRecorder();
        initCameraDeviceStateCallback();
        initSessionStateCallback();
        initSessionCaptureCallback();
    }

    private fun initClickListener() {
        mBtnStatr!!.setOnClickListener(object : View.OnClickListener {
            @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
            override fun onClick(v: View?) {
                config()
                startRecorder()
            }
        })
        mBtnFinish!!.setOnClickListener(object : View.OnClickListener {
            override fun onClick(v: View?) {
                stopRecorder()
            }
        })
    }
    private fun initCameraDeviceStateCallback() {
        mCameraDeviceStateCallback = @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
        object : CameraDevice.StateCallback() {
            override fun onOpened(camera: CameraDevice) {
                //摄像头被打开
                try {
                    mCameraDevice = camera
                    val cameraSize: Size? = getMatchingSize2() //计算获取需要的摄像头分辨率
                    val surfaceTexture = mTextureView!!.surfaceTexture //得到纹理
                    surfaceTexture!!.setDefaultBufferSize(
                        1920,
                        1080
                    )
                    val previewSurface = Surface(surfaceTexture)
                    mPreviewCaptureRequest =
                        mCameraDevice!!.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
                    mPreviewCaptureRequest!!.set(
                        CaptureRequest.CONTROL_AF_MODE,
                        CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE
                    )
                    mPreviewCaptureRequest!!.addTarget(previewSurface)
                    var fpsRange = Range(120,120)
                    mPreviewCaptureRequest!!.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,fpsRange)
                    mCameraDevice!!.createConstrainedHighSpeedCaptureSession(
                        Arrays.asList(previewSurface),
                        mSessionStateCallback!!,
                        mChildHandler
                    ) //创建数据捕获会话，用于摄像头画面预览，这里需要等待mSessionStateCallback回调
                } catch (e: CameraAccessException) {
                    e.printStackTrace()
                }
            }

            override fun onDisconnected(camera: CameraDevice) {
                //摄像头断开
            }

            override fun onError(camera: CameraDevice, error: Int) {
                //异常
            }
        }
    }
    private fun initSessionStateCallback() {
        mSessionStateCallback = @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
        object : CameraCaptureSession.StateCallback() {
            override fun onConfigured(session: CameraCaptureSession) {
                mCameraCaptureSession = session
                try {
                    val request: CaptureRequest = mPreviewCaptureRequest!!.build()
                    val slowMoRequests =
                        (mCameraCaptureSession as CameraConstrainedHighSpeedCaptureSession)
                            .createHighSpeedRequestList(request)
                    //执行重复获取数据请求，等于一直获取数据呈现预览画面，mSessionCaptureCallback会返回此次操作的信息回调
                    mCameraCaptureSession!!.setRepeatingBurst(
                        slowMoRequests,
                        mSessionCaptureCallback,
                        mChildHandler
                    )
                } catch (e: CameraAccessException) {
                    e.printStackTrace()
                }
            }

            override fun onConfigureFailed(session: CameraCaptureSession) {}
        }
    }
    private fun initSessionCaptureCallback() {
        mSessionCaptureCallback = @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
        object : CaptureCallback() {
            override fun onCaptureStarted(
                session: CameraCaptureSession,
                request: CaptureRequest,
                timestamp: Long,
                frameNumber: Long
            ) {
                super.onCaptureStarted(session, request, timestamp, frameNumber)
            }

            override fun onCaptureProgressed(
                session: CameraCaptureSession,
                request: CaptureRequest,
                partialResult: CaptureResult
            ) {
                super.onCaptureProgressed(session, request, partialResult)
            }

            override fun onCaptureCompleted(
                session: CameraCaptureSession,
                request: CaptureRequest,
                result: TotalCaptureResult
            ) {
                super.onCaptureCompleted(session, request, result)
            }

            override fun onCaptureFailed(
                session: CameraCaptureSession,
                request: CaptureRequest,
                failure: CaptureFailure
            ) {
                super.onCaptureFailed(session, request, failure)
            }
        }
    }
    /**
     * 初始化子线程Handler，操作Camera2需要一个子线程的Handler
     */
    private fun initChildHandler() {
        val handlerThread = HandlerThread("Camera2Demo")
        var mTextureView = findViewById<TextureView>(R.id.textureview2)
        handlerThread.start()
        mChildHandler = Handler(handlerThread.looper)
    }

    /**
     * 初始化TextureView的纹理生成监听，只有纹理生成准备好了。我们才能去进行摄像头的初始化工作让TextureView接收摄像头预览画面
     */
    private fun initTextureViewStateListener() {
        mTextureView?.setSurfaceTextureListener(object : SurfaceTextureListener {
            @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
            override fun onSurfaceTextureAvailable(
                surface: SurfaceTexture,
                width: Int,
                height: Int
            ) {
                //可以使用纹理
                initCameraManager()
                selectCamera()
                openCamera()
                test()
            }

            override fun onSurfaceTextureSizeChanged(
                surface: SurfaceTexture,
                width: Int,
                height: Int
            ) {
                //纹理尺寸变化
            }

            override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean {
                //纹理被销毁
                return false
            }

            override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {
                //纹理更新
            }
        })
    }

    /**
     * 初始化Camera2的相机管理，CameraManager用于获取摄像头分辨率，摄像头方向，摄像头id与打开摄像头的工作
     */
    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    private fun initCameraManager() {
        mCameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
    }

    /**
     * 选择一颗我们需要使用的摄像头，主要是选择使用前摄还是后摄或者是外接摄像头
     */
    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    private fun selectCamera() {
        if (mCameraManager != null) {
            Log.e(TAG, "selectCamera: CameraManager is null")
        }
        try {
            val cameraIdList = mCameraManager!!.cameraIdList //获取当前设备的全部摄像头id集合
            if (cameraIdList.size == 0) {
                Log.e(TAG, "selectCamera: cameraIdList length is 0")
            }
            for (cameraId in cameraIdList) { //遍历所有摄像头
                val characteristics =
                    mCameraManager!!.getCameraCharacteristics(cameraId) //得到当前id的摄像头描述特征
                val facing = characteristics.get(CameraCharacteristics.LENS_FACING) //获取摄像头的方向特征信息
                if (facing == CameraCharacteristics.LENS_FACING_BACK) { //这里选择了后摄像头
                    mCurrentSelectCamera = cameraId
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    /**
     * 打开摄像头，这里打开摄像头后，我们需要等待mCameraDeviceStateCallback的回调
     */
    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    @SuppressLint("MissingPermission")
    private fun openCamera() {
        try {
            mCameraManager!!.openCamera(
                mCurrentSelectCamera!!,
                mCameraDeviceStateCallback!!,
                mChildHandler
            )
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    /**
     * 计算需要的使用的摄像头分辨率
     * @return
     */
    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    private fun getMatchingSize2(): Size? {
        var selectSize: Size? = null
        try {
            val cameraCharacteristics = mCameraManager!!.getCameraCharacteristics(
                mCurrentSelectCamera!!
            )
            val streamConfigurationMap =
                cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
            val sizes = streamConfigurationMap!!.getOutputSizes(ImageFormat.JPEG)
            val displayMetrics = resources.displayMetrics //因为我这里是将预览铺满屏幕,所以直接获取屏幕分辨率
            val deviceWidth = displayMetrics.widthPixels //屏幕分辨率宽
            val deviceHeigh = displayMetrics.heightPixels //屏幕分辨率高
            Log.e(TAG, "getMatchingSize2: 屏幕密度宽度=$deviceWidth")
            Log.e(TAG, "getMatchingSize2: 屏幕密度高度=$deviceHeigh")
            /**
             * 循环40次,让宽度范围从最小逐步增加,找到最符合屏幕宽度的分辨率,
             * 你要是不放心那就增加循环,肯定会找到一个分辨率,不会出现此方法返回一个null的Size的情况
             * ,但是循环越大后获取的分辨率就越不匹配
             */
            for (j in 1..40) {
                for (i in sizes.indices) { //遍历所有Size
                    val itemSize = sizes[i]
                    Log.e(TAG, "当前itemSize 宽=" + itemSize.width + "高=" + itemSize.height)
                    //判断当前Size高度小于屏幕宽度+j*5  &&  判断当前Size高度大于屏幕宽度-j*5  &&  判断当前Size宽度小于当前屏幕高度
                    if (itemSize.height < deviceWidth + j * 5 && itemSize.height > deviceWidth - j * 5) {
                        if (selectSize != null) { //如果之前已经找到一个匹配的宽度
                            if (Math.abs(deviceHeigh - itemSize.width) < Math.abs(deviceHeigh - selectSize.width)) { //求绝对值算出最接近设备高度的尺寸
                                selectSize = itemSize
                                continue
                            }
                        } else {
                            selectSize = itemSize
                        }
                    }
                }
                if (selectSize != null) { //如果不等于null 说明已经找到了 跳出循环
                    break
                }
            }
        } catch (e: java.lang.Exception) {
            e.printStackTrace()
        }
        Log.e(TAG, "getMatchingSize2: 选择的分辨率宽度=" + selectSize!!.width)
        Log.e(TAG, "getMatchingSize2: 选择的分辨率高度=" + selectSize.height)
        return selectSize
    }

    /**
     * 初始化MediaRecorder
     */
    private fun initMediaRecorder() {
        mMediaRecorder = MediaRecorder()
    }

    /**
     * 配置录制视频相关数据
     */
    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    private fun configMediaRecorder() {
        val file = File(externalCacheDir, "demo.mp4")
        Log.e(TAG, file.absolutePath)
        if (file.exists()) {
            file.delete()
        }
        mMediaRecorder!!.setAudioSource(MediaRecorder.AudioSource.MIC) //设置音频来源
        mMediaRecorder!!.setVideoSource(MediaRecorder.VideoSource.SURFACE) //设置视频来源
        mMediaRecorder!!.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT) //设置输出格式
        mMediaRecorder!!.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT) //设置音频编码格式，请注意这里使用默认，实际app项目需要考虑兼容问题，应该选择AAC
        mMediaRecorder!!.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT) //设置视频编码格式，请注意这里使用默认，实际app项目需要考虑兼容问题，应该选择H264
        mMediaRecorder!!.setVideoEncodingBitRate(120 * 1024 * 1920) //设置比特率 一般是 1*分辨率 到 10*分辨率 之间波动。比特率越大视频越清晰但是视频文件也越大。
        mMediaRecorder!!.setVideoFrameRate(120) //设置帧数 选择 30即可， 过大帧数也会让视频文件更大当然也会更流畅，但是没有多少实际提升。人眼极限也就30帧了。
        val size = getMatchingSize2()
        mMediaRecorder!!.setVideoSize(1920, 1080)
        mMediaRecorder!!.setOrientationHint(90)
        val surface = Surface(mTextureView!!.surfaceTexture)
        mMediaRecorder!!.setPreviewDisplay(surface)
        mMediaRecorder!!.setOutputFile(file.getAbsolutePath())
        try {
            mMediaRecorder!!.prepare()
        } catch (e: java.lang.Exception) {
            e.printStackTrace()
        }
    }

    /**
     * 开始录制视频
     */
    private fun startRecorder() {
        mMediaRecorder!!.start()
    }

    /**
     * 暂停录制视频（暂停后视频文件会自动保存）
     */
    private fun stopRecorder() {
        mMediaRecorder!!.stop()
        mMediaRecorder!!.reset()
    }

    /**
     * 重新配置录制视频时的CameraCaptureSession
     */
    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    private fun config() {
        try {
            mCameraCaptureSession!!.stopRepeating() //停止预览，准备切换到录制视频
            mCameraCaptureSession!!.close() //关闭预览的会话，需要重新创建录制视频的会话
            mCameraCaptureSession = null
        } catch (e: Exception) {
            e.printStackTrace()
        }
        configMediaRecorder()
        val cameraSize = getMatchingSize2()
        val surfaceTexture = mTextureView!!.surfaceTexture
//        surfaceTexture!!.setDefaultBufferSize(cameraSize!!.width, cameraSize.height)
        surfaceTexture!!.setDefaultBufferSize(1920,1080)
        val previewSurface = Surface(surfaceTexture)
        val recorderSurface = mMediaRecorder!!.surface //从获取录制视频需要的Surface
        try {
            mPreviewCaptureRequest =
                mCameraDevice!!.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
            mPreviewCaptureRequest!!.set(
                CaptureRequest.CONTROL_AF_MODE,
                CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE
            )
            mPreviewCaptureRequest!!.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,Range(120,120))
            mPreviewCaptureRequest!!.addTarget(previewSurface)
            mPreviewCaptureRequest!!.addTarget(recorderSurface)
            //请注意这里设置了Arrays.asList(previewSurface,recorderSurface) 2个Surface，很好理解录制视频也需要有画面预览，第一个是预览的Surface，第二个是录制视频使用的Surface
//            mCameraDevice!!.createCaptureSession(
//                Arrays.asList(previewSurface, recorderSurface),
//                mSessionStateCallback!!, mChildHandler
//            )
            mCameraDevice!!.createConstrainedHighSpeedCaptureSession(
                Arrays.asList(previewSurface, recorderSurface),
                mSessionStateCallback!!, mChildHandler)
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    fun test(){
        val characteristics: CameraCharacteristics = mCameraManager!!.getCameraCharacteristics(mCurrentSelectCamera!!)
        val map = characteristics
            .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)


        for (fpsRange in map!!.highSpeedVideoFpsRanges) {
            Log.d(TAG, "openCamera: [width, height] = $fpsRange")
        }
    }

}